package ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;

import structures.KvComboCellEditor;
import structures.Project;
import structures.ProjectListEntry;
import structures.Task;
import structures.TaskTableModel;
import structures.TestRenderer;

import model.ProjectDBModel;

/**
 * A text ui for testing projectdb.
 * @author darin
 *
 */
public class SwingUI implements Observer, ProjectdbUI, ActionListener,
ListSelectionListener, TableModelListener, KeyListener{

	/* CONSTANTS  AND DEFAULTS */
	/**
	 * initial window size.
	 */
	private static final Dimension FRAME_SIZE = new Dimension(800, 600);
	/**
	 * Set the location to store icons.
	 */
	private static final String ICON_LOCATION = "../resources/icons";
	/* FIELDS */

	/**
	 * So the main frame is available to various dialogs as a parent
	 */
	private JFrame frame;
	/**
	 * an instance of ProjectDBModel to handle database interaction.
	 */
	private ProjectDBModel model;
	/**
	 * HOlds the list of projects, global so listeners have access to.
	 * make changes to it.
	 */
	private JList<ProjectListEntry>  projectList;

	/**
	 * A dialog box for entering new project information.
	 */
	private ProjectEntryDialog projectEntryDialog;

	/**
	 *  Displays the project id in the right pane.
	 */
	private JTextField projectIdTextField;
	/**
	 *  displays the project Name in the right panel
	 */
	private JTextField projectNameTextField;
	/**
	 *  displays the project description in the right panel
	 */
	private JTextField projectDescriptionTextField;

	private JComboBox typeCombo;
	
	private JComboBox subTypeCombo;
	/**
	 *  so I can print dates in a pretty way.
	 *  probably outdated since I'm using sql dates everywhere
	 */
	private SimpleDateFormat df = new SimpleDateFormat("YYYY-MM-dd");
	/**
	 * to help make pretty dates, see above.
	 */
	private Calendar calendar = new GregorianCalendar();
	/**
	 * So I can add and remove things from the projectSrollPane
	 */
	private JScrollPane projectScrollPane;
	/**
	 *  So I can put things into the table scroll pane
	 *  (like tables)
	 */
	private JScrollPane tableScrollPane;
	/**
	 * So I can manipulate the task table
	 */
	private JTable taskTable;

	/* CONSTRUCTORS */
	/**
	 * the constructor.
	 * @param m an instance of projectDBModel passed from main.
	 */
	public SwingUI(final ProjectDBModel m) {
		model = m;
		model.addObserver(this);
		run();
	}

	/**
	 * Gets the ball rolling.
	 */
	public final void run() {
		//populate the project list
		//Make and show the user interface

		frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setPreferredSize(new Dimension(FRAME_SIZE));
		// add a base panel to the frame
		JPanel basePanel  = new JPanel();
		frame.add(basePanel);
		basePanel.setLayout(new BorderLayout());
		// split the main window into scroll pane of projects on the
		//  left and something with a table in a scrollpane on the right
		JSplitPane splitPane = new JSplitPane(
				JSplitPane.HORIZONTAL_SPLIT);
		basePanel.add(splitPane, BorderLayout.CENTER);
		// Left and right panels to hold menus and content
		// left gets a tool bar, a scroll pane containing a list pane
		JPanel leftPanel = buildLeftPanel();
		JPanel rightPanel = buildRightPanel();

		splitPane.setLeftComponent(leftPanel);
		splitPane.setRightComponent(rightPanel);

		basePanel.add(splitPane, BorderLayout.CENTER);
		populateProjectList();
		frame.pack();
		frame.setVisible(true);
	}

// UI BUILDING HELPER METHODS
	/**
	 * Builds Buttons for project list tool bar.
	 * @param imageName the name of a gif for the button image
	 * @param actionCommand Will have to look into this
	 * @param toolTipText Nice text for when you hover over the button
	 * @param altText what if there's no image
	 * @return JButton a button with the above features
	 */
	private JButton makeToolBarButton(final String imageName,
            final String actionCommand,
            final String toolTipText,
            final String altText) {

		JButton button = new JButton();
		button.setActionCommand(actionCommand);
		button.setToolTipText(toolTipText);
		button.addActionListener(this);
		URL imageURL = SwingUI.class.getResource(ICON_LOCATION);
		if (imageURL != null) {
			button.setIcon(new ImageIcon(imageURL, altText));
		} else {
			button.setText(altText);
		}
		return button;
	}

	/**
	 *  builds the left panel with tool bar and a scrolling list pane.
	 * @return JPanel a populated JPanel
	 */
	private JPanel buildLeftPanel()	{
		JPanel leftPanel = new JPanel();
		// in order for tool bar drag features to work it must go into a
		//container that users borderLayout, must be the only other item
		//center in the panel besides the center item, and must not be
		// the item.
		leftPanel.setLayout(new BorderLayout());
		JToolBar projectToolBar = new JToolBar();
		// create and add a "new" button to the tool bar
		projectToolBar.add(makeToolBarButton(
				"newProject.gif",
				"new Project",
				"Create a new Project",
				"New"));
		// create and add a "edit" button to the tool bar
		projectToolBar.add(makeToolBarButton(
				"editProject.gif",
				"Edit Project",
				"Edit current Project",
				"Edit"));
		// create and add a "delete" button to the tool bar
		projectToolBar.add(makeToolBarButton(
				"deleteProject.gif",
				"delete Project",
				"Delete current Project",
				"Delete"));
		// add the tool bar to the left panel
		leftPanel.add(projectToolBar, BorderLayout.NORTH);
		// now add a scroll pane to the left panel, layout center
		projectScrollPane = new JScrollPane();
		leftPanel.add(projectScrollPane, BorderLayout.CENTER);
		// make the list pane -- note, the JList can be type Object[]
		projectList = new JList<ProjectListEntry>();
		
		projectList.setSelectionMode(
				ListSelectionModel.SINGLE_SELECTION);
		
		projectList.addListSelectionListener(this);
		// set the left scroll pane to show the project list
		projectScrollPane.setViewportView(projectList);

		return leftPanel;
	}

	/**
	 * Builds the right panel with an area at the top to show details.
	 * of the currently selected project and an area below to show a
	 * scrollable table of task entries
	 * @return a populated JPanel
	 */
	private JPanel buildRightPanel() {
		// the base panel to hold both sub-panels
		JPanel rightPanel = new JPanel();
		rightPanel.setLayout(new BorderLayout());
		// build the project details panel
		rightPanel.add(buildProjectDetailsPanel(), BorderLayout.NORTH);
		tableScrollPane = new JScrollPane(); // empty for now fill later
		rightPanel.add(tableScrollPane, BorderLayout.CENTER);

		
		return rightPanel;
	}

	/**
	 * builds the project details panel for the top of the right side
	 * of the split screen.
	 * @return a populated JPanel for the top right side of screen
	 */
	private JPanel buildProjectDetailsPanel() {
		JPanel projectDetailsPanel = new JPanel();
		// flowLayout probably isn't the best choice, but
		//it'll get me started

		JPanel idAndNamePanel = new JPanel();

		JLabel projectIdLabel = new JLabel("ID");
		idAndNamePanel.add(projectIdLabel);
		
		// TODO turn this into a JLabel
		projectIdTextField = new JTextField(2);
		idAndNamePanel.add(projectIdTextField);

		JLabel projectNameLabel = new JLabel("Project Name");
		idAndNamePanel.add(projectNameLabel);

		projectNameTextField = new JTextField(10);
		projectNameTextField.addActionListener(this);
		projectNameTextField.setActionCommand("Project Name Edited");
		idAndNamePanel.add(projectNameTextField);
		projectDetailsPanel.add(idAndNamePanel);

		JPanel projectDescriptionPanel = new JPanel();
		projectDescriptionPanel.add(new JLabel("Description"));
		projectDescriptionTextField = new JTextField(25);
		projectDescriptionPanel.add(projectDescriptionTextField);
		projectDetailsPanel.add(projectDescriptionPanel);

		return projectDetailsPanel;
	}

	

// **************** handle changes in the model ****************** //
	@Override
	public final void update(final Observable who, final Object what) {
		if (what.equals("Projects")) {
			populateProjectList();
		}
		if (what.equals("Tasks")) {
			buildTaskTable(
					Integer
					.parseInt(
							projectIdTextField
							.getText()));
		}
	}

	/**
	 * Added by ActionListener, responds to button clicks on the tool bar.
	 * buttons
	 * @param e the action event
	 */
	@Override
	public final void actionPerformed(final ActionEvent e) {
		System.out.println(e);
		if (e.getActionCommand().equals("new Project")) {
			if (projectEntryDialog == null) {
				projectEntryDialog = new ProjectEntryDialog();
				projectEntryDialog.setModal(true);
			}
			projectEntryDialog.setVisible(true);
			// setVisible blocks until dialog is closed
			if (projectEntryDialog.getFlag()) {
				// user wants to make new project entry
				createProject(
					projectEntryDialog.getProjectName(),
					projectEntryDialog.
					   getProjectDescription());
			}
		}
		if (e.getActionCommand().equals("delete Project")) {
			if (projectList.getSelectedValue() != null) {
				deleteProject(projectList.getSelectedValue());
			}
		}
		
		if(e.getActionCommand().equals("Edit Project"))
		{
			// pop up a dialog with the project information
			// update db when dialog is changed.
			System.out.println("edit project button clicked");
		}
		
		// What calls "new task"?
		/*
		if (e.getActionCommand().equals("New Task")) {

			int currentProject;
			if (projectIdTextField.getText().equals("")) {
				System.out.println("select a project first");
			} else {
				currentProject = Integer.parseInt(
						projectIdTextField.getText());
				System.out.println(
						"Make a new task for project "
						+ currentProject);
			}
		}
		*/
		
		// what calls "Delete Task"?
		/*
		if (e.getActionCommand().equals("Delete Task")) {
			int currentProject;
			if (projectIdTextField.getText().equals("")
				|| taskTable.getSelectedRow() < 0) {
				System.out.println(
					"select a project and task first");
			} else {
				currentProject =
					Integer.parseInt(
						projectIdTextField.getText());
				int currentRow = taskTable.getSelectedRow();
				int currentTask =
						(int) taskTable.getValueAt(
								currentRow, 0);
				System.out.println(
						"Delete Task "
				+ currentTask
				+ " from project "
				+ currentProject);
			}
		}
		*/
	}

	/**
	 * added by ListSelectionListener, repsonds to mouse clicks in the.
	 * project list
	 * @param e an event
	 */
	@Override
	public final void valueChanged(final ListSelectionEvent e) {
		if (!e.getValueIsAdjusting()) {
			if (projectList.getSelectedValue() != null) {
				setCurrentProject(
						projectList
						.getSelectedValue()
						.getProjectId());
			}
		}
	}

// HELPER MEHTODS
	/**
	 * Create project here so try/catch block isn't cluttering up the event.
	 * handling code
	 * @param name the name of the project to create
	 * @param desc a description of the project
	 */
	private void createProject(final String name, final String desc) {
		try {
			model.createNewProject(
					projectEntryDialog.getProjectName(),
					projectEntryDialog
					.getProjectDescription(),
					df.format(calendar.getTime()));
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * gets the project names from the database.
	 * TODO Instead of string, this should be type Project and should
	 * contain at least name and ID so I have a unique identifier. Setting
	 * Project's toString to print the name should display
	 *  the correct results
	 */
	private void populateProjectList() {
		ArrayList<ProjectListEntry> projectNames = null;
		try {
			projectNames = model.getProjectNames();
		} catch (InstantiationException | IllegalAccessException
				| ClassNotFoundException | SQLException e) {
			//TODO handle these errors more gracefully
			e.printStackTrace();
		}
		DefaultListModel<ProjectListEntry> newModel =
				new DefaultListModel<ProjectListEntry>();
		for (int i = 0; i < projectNames.size(); i++) {
			newModel.addElement(projectNames.get(i));
		}
		projectList.setModel(newModel);
	}

	/**
	 * sets the textFields in the right panel details area to.
	 * reflect the information for the current project
	 * @param id the id of the current project
	 */
	private void setCurrentProject(final int id) {
		Project currentProject = null;
		try {
			currentProject = model.getProject(id);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		projectIdTextField.setText(
				Integer.toString(
						currentProject.getProjectId()));
		projectNameTextField.setText(
				currentProject.getProjectName());
		this.projectDescriptionTextField.setText(
				currentProject.getProjectDesc());
		buildTaskTable(currentProject.getProjectId());
		// now create a table and put it into the right scrollpane.
	}

	/**
	 * Delete a project and all its tasks from the db.
	 * @param entry the project list entry you want to delete
	 */
	private void deleteProject(final ProjectListEntry entry) {
		// pop up a confirm dialog
		// if yes, delet the project and all tasks
		// otherwise do nothing.
		int option = JOptionPane.showConfirmDialog(frame,
				"Do you really want to delete\n Project: ["
				+ entry.getProjectId() + "] "
				+ entry.getProjectName()
				+ " \n2and all of its tasks?! ",
				"Realy Delete Project?",
				JOptionPane.YES_NO_OPTION,
				JOptionPane.WARNING_MESSAGE);
		if (option == JOptionPane.YES_OPTION) {
			deleteProject(entry.getProjectId());
		}
	}

	/**
	 * Tells the model to delete the project.
	 * this process removed from the event handler code to prevent
	 * clutter
	 * @param id the id of the project to be deleted.
	 */
	private void deleteProject(final int id) {
		try {
			model.deleteProject(id);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 *  builds and installs the task table.
	 * @param projectId the id of the currently selected project.
	 */
	private void buildTaskTable(final int projectId) {
		ArrayList<Task> tasks = null;
		ArrayList<String> colNames = null;
		try {
			tasks = model.getTasks(projectId);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// get the hashmaps for type and subtype
		HashMap<Integer, String> typeMap = getTypeMap();
		HashMap<Integer, String> subtypeMap = getSubtypeMap();
		// build the table and add custom model
		taskTable = new JTable(new TaskTableModel(tasks));
		taskTable.getModel().addTableModelListener(this);
		taskTable.addKeyListener(this);
		//TableCellRenderer renderer = new KvItemRenderer();
		taskTable.getColumnModel().getColumn(4).setCellRenderer(
				new TestRenderer(typeMap));
		taskTable.getColumnModel().getColumn(5).setCellRenderer(
				new TestRenderer(subtypeMap));

		// make and add the combobox editors for the
		//type and subtype cols
		TableColumn typeCol = taskTable.getColumnModel().getColumn(4);
		typeCol.setCellEditor(new KvComboCellEditor(typeMap));
		TableColumn subtypeCol =
				taskTable.getColumnModel().getColumn(5);
		subtypeCol.setCellEditor(new KvComboCellEditor(subtypeMap));
		// add the table to the panel
		tableScrollPane.setViewportView(taskTable);
	}
	/**
	 * 
	 * @return a hashmap of types
	 */
	private HashMap<Integer, String> getTypeMap() {
		HashMap<Integer, String> typeMap = null;
		try {
			typeMap = model.getTypeMap();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return typeMap;
	}
	/**
	 *
	 * @return a hashmap containing subtypes
	 */
	private HashMap<Integer, String> getSubtypeMap() {
		HashMap<Integer, String> subtypeMap = null;
		try {
			subtypeMap = model.getSubtypeMap();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return subtypeMap;
	}

	@Override
	public void tableChanged(final TableModelEvent e) {
		System.out.println("row " + e.getFirstRow() + " changed");
		int row = e.getFirstRow();
		Task task = new Task();
		task.setProjectId(
				Integer.parseInt(
						projectIdTextField.getText()));
		task.setTaskId((int) taskTable.getValueAt(row, 0));
		task.setTaskDate((Date) taskTable.getValueAt(row, 1));
		task.setTimeIn((Time) taskTable.getValueAt(row, 2));
		task.setTimeOut((Time) taskTable.getValueAt(row, 3));
		task.setType((int) taskTable.getValueAt(row, 4));
		task.setSubtype((int) taskTable.getValueAt(row, 5));
		task.setTaskDesc((String) taskTable.getValueAt(row,6));
		try {
			if(task.getTaskId() > 0)
				model.updateTask(task);
			else if(task.getTaskId() == 0 &&  
					task.getProjectId() != 0 &&
					task.getSubtype() != 0 &&
					task.getType() != 0 &&
					task.getTaskDate() != null &&
					task.getTimeIn() != null &&
					task.getTimeOut()!= null &&
					task.getTaskDesc() != null)
				model.insertTask(task);
		} catch (IllegalAccessException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (ClassNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InstantiationException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
		if(e.getKeyChar() == KeyEvent.VK_DELETE)
		{
			System.out.println("Delete key pressed");
			int row = taskTable.getSelectedRow();
			int col = 0; // because we want the id field
			int id = (int) taskTable.getModel().getValueAt(row, col);
			try {
				if(id > 0)
				model.deleteTask(id);
			} catch (IllegalAccessException | ClassNotFoundException
					| InstantiationException | SQLException e1) {
				e1.printStackTrace();
			}
		}
		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}
}
